package rerac.components;

import goalie.Component;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import rerac.protos.InstanceCollection.Instance;
import rerac.protos.InstanceCollection.Instance.Feature;

/**
 * 
 * This brings instances of the Instance protocol buffer format into a format
 * recognized by classification toolkits like LibSvm or SvmLight, i.e. a
 * sparse format with lines for each instance of the form:
 * 
 * class_label feature_indexA feature_value feature_indexB feature_value ...
 * 
 * Feature indices are given in increasing order.
 * 
 * @author Benjamin Roth
 *
 */
public class ClassifierFeatureFormat implements Component {
  
  public static final String REFERRED_NAME = "classifier_format";
  
  @Override
  public void cleanup(Map<String, String> outputs) throws IOException {
    for (String fn : outputs.get("output").split(",")) {
      File f = new File(fn);
      f.delete();
    }
  }

  @Override
  public Map<String, String> run(Map<String, String> params) throws IOException {
    String instanceFN = params.get("input");
    String svmFeaturePath = params.get("outpath");
    String[] categories = params.get("categories").split(",");
    boolean useOtherCategories = params.containsKey("use_other_categories") ?
        Boolean.parseBoolean(params.get("use_other_categories")) : true;
          
    StringBuffer outputSB = new StringBuffer();
    for (String category : categories) {
      BufferedInputStream is = new BufferedInputStream(new FileInputStream(
          instanceFN));
      String svmFeatureFN = svmFeaturePath + "/" + category + ".svm";
      if (outputSB.length() > 0) {
        outputSB.append(",");
      }
      outputSB.append(svmFeatureFN);
      
      BufferedWriter bw = new BufferedWriter(new FileWriter(svmFeatureFN));
  
      for (Instance inst; (inst = Instance.parseDelimitedFrom(is)) != null;) {
        boolean positive = false;
        if (inst.hasCategory() && 
            inst.getCategory().equals(category) &&
            inst.getPositive()) {
          positive = true;
        }
        if ((!inst.hasCategory() || !inst.getCategory().equals(category)) && 
            !useOtherCategories) {
          continue;
        }
        bw.write(positive ? "+1" : "-1");
        for (Feature f : inst.getFeatureList()) {
          bw.write(" ");
          bw.write(Integer.toString(f.getIndex()));
          bw.write(":");
          bw.write(Double.toString(f.getValue()));
        }
        bw.newLine();
      }
      bw.close();
      is.close();
    }

    Map<String, String> outMap = new HashMap<String, String>();
    outMap.put("output", outputSB.toString());
    return outMap;
  }
}
